using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using MirrorMirror;

namespace Magiq.Support
{
    public class EnumerableSource
    {
        public IList List { get; private set; }
        public bool IsQuery { get { return enumerableCalledTwiceOrMore || queryableChain; } }
        private bool enumerableCalledTwiceOrMore;
        private bool queryableChain;

        public EnumerableSource(IEnumerable items)
        {
            UpdateSourceWith(items);
        }
 
        private void UpdateSourceWith(IEnumerable items)
        {
            var list = items as IList;
            if (list != null)
            {
                if (list.IsFixedSize)
                    throw new SourceIsFixedSizeException(list);

                List = list;
                return;
            }
            
            var queryable = items as IQueryable;
            if (queryable != null)
            {
                UpdateSourceFromQueryProvider(queryable);
                return;
            }
            
            var parent = items.Get("source") as IList;

            if (parent == null)
                throw new SourceIsFixedSizeException(queryable);

            enumerableCalledTwiceOrMore = true;
            UpdateSourceWith(parent);
        }

        private void UpdateSourceFromQueryProvider(IQueryable queryable)
        {
            var constant = ConstantFrom(queryable.Expression);

            if (constant == null)
                throw new CannotResolveSourceException(queryable);

            var value = constant.Value.Get("enumerable") as IEnumerable;
            UpdateSourceWith(value);
        }

        private ConstantExpression ConstantFrom(Expression expression)
        {
            var constant = expression as ConstantExpression;

            if (constant != null)
                return constant;
            queryableChain = true;
            var call = expression as MethodCallExpression;

            return call == null ? null : ConstantFrom(call.Arguments[0]);
        }
    }
}